<?php

Raise::load('core.RaiseObject');
Raise::load('core.RaiseApplication');
Raise::load('core.RaiseCollection');
Raise::load('core.RaiseHttpRequest');
Raise::load('core.RaiseFile');
Raise::load('core.RaiseMime');

/**
 * RaiseFileUpload class
 * provides functionality to process and upload files
 *
 * @author Sam-Mauris Yong <hellclanner at live dot com>
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package Raise.Core.DataStructure
 * @since 1.2
 */
class RaiseFileUpload extends RaiseObject {

    /**
     * The RaiseApplication that this RaiseFileUpload is handling uploads for
     * @var RaiseApplication
     */
    private $application;

    /**
     * A collection of uploaded files
     * @var RaiseCollection
     */
    private $files;

    /**
     * A collection of accepted MIME types
     * @var RaiseCollection
     */
    private $mime;

    /**
     * A collection of accepted file extensions
     * @var RaiseCollection
     */
    private $extension;

    /**
     * The maximum size of each file measured in bytes
     * @var integer
     */
    private $maxSize;

    /**
     * Path to the file upload destionation
     * @var string
     */
    private $path;

    /**
     * Create a new File Upload handler
     */
    public function __construct($path) {
        $this->path($path);
        $this->mime = new RaiseCollection();
        $this->extension = new RaiseCollection();
        $a = new RaiseCollection($_FILES);
        if (!$a) {
            $a = new RaiseCollection();
        }
        foreach ($a as $k => $b) {
            $a->add($k, self::normalize(new RaiseCollection($b)));
        }
        $this->files = $a;
    }

    /**
     * Get or set the RaiseApplication that started this page
     * @param RaiseApplication $app (optional) If set, the new value will be set.
     * @return RaiseApplication
     */
    public function application($app = null) {
        if (func_num_args() == 1) {
            if (!($app instanceof RaiseApplication)) {
                throw new RaiseInvalidArgumentException('RaiseFileUpload::application() expecting $app to be an instance of RaiseApplication, ' . RaiseVariable::typeInfo($app) . ' given instead.');
            }
            $this->application = $app;
        }
        return $this->application;
    }

    /**
     * Get or set the maximum size of each file
     * @param integer $m (optional) If set, the new maximum size will be set to $m
     * @return integer
     */
    public function maxSize($m = null) {
        if (func_num_args() == 1) {
            $this->maxSize = $m;
        }
        return $this->maxSize;
    }

    /**
     * Get or set the path to the directory to save the uploaded files
     * @param string $p (optional) If set, the new path will be set
     * @return string
     */
    public function path($p = null) {
        if (func_num_args() == 1) {
            $this->path = $p;
        }
        return $this->path;
    }

    /**
     * Get the collection of acceptable MIME types
     * @return RaiseCollection
     */
    public function mime() {
        return $this->mime;
    }

    /**
     * Get the collection of acceptable file extensions
     * @return RaiseCollection
     */
    public function extension() {
        return $this->extension;
    }

    public function process() {
        if ($this->application()->request()->method() == RaiseHttpRequest::METHOD_POST) {
            if ($this->files->count() > 0) {
                $files = $this->processEntry($this->files);
                $this->files->clear();
                return $files;
            }
        } elseif ($this->application()->request()->method() == RaiseHttpRequest::METHOD_PUT) {
            // feature coming soon
        }
    }

    /**
     * Process a collection of pending files
     * @param RaiseCollection $entry
     * @return RaiseFile
     */
    private function processEntry($entry) {
        if ($entry instanceof RaiseCollection) {
            if ($entry->keyExists('name')) {
                if($entry->get('error') == UPLOAD_ERR_OK){
                    $ext = RaisePath::extension($entry->get('name'));
                    $mime = $entry->get('type');
                    
                    if ($this->mime()->valueExists($mime) && $this->extension()->valueExists($ext) && ($entry->get('size') < $this->maxSize || $this->maxSize == 0)) {

                        $destFile = RaisePath::combine($this->path, $entry->get('name'));
                        $i = 0;
                        while (RaiseFileSystem::fileExist($destFile)) {
                            $i++;
                            $destFile = RaisePath::combine($this->path, RaisePath::fileName($entry->get('name')) . '-' . $i . '.' . $ext);
                        }
                        unset($i);
                        move_uploaded_file($entry->get('tmp_name'), $destFile);
                        return new RaiseFile($destFile);
                    }
                }
                return null;
            } else {
                $files = new RaiseCollection();
                foreach ($entry as $k => $e) {
                    $e = new RaiseCollection($e);
                    $file = $this->processEntry($e);
                    if ($file) {
                        $files->add($k, $file);
                    }
                }
                return $files;
            }
        }
    }

    /**
     *
     * @param RaiseCollection $entry
     * @return RaiseCollection
     * @static
     */
    private static function normalize($entry) {
        if ($entry->keyExists('name') && $entry->get('name') instanceof RaiseCollection) {
            $files = new RaiseCollection();
            foreach ($entry->get('name') as $k => $name) {
                $files->add($k, array(
                    'name' => $name,
                    'tmp_name' => $entry->get('tmp_name')->get($k),
                    'size' => $entry->get('size')->get($k),
                    'type' => $entry->get('type')->get($k),
                    'error' => $entry->get('error')->get($k)
                ));
            }
            return self::normalize($files);
        }
        return $entry;
    }

}